//onemax problem-depended class
//$Id: onemax.h 722 2012-11-29 17:33:43Z andrew.tsyganov $
#pragma once

#include "src/heo/include/random.h"
#include "src/heo/include/calc_hash.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/transmit_array.h"
#include "src/heo/include/fast_stream.h"
#include "src/heo/include/lf_endl.h"
#include "src/heo/include/stream_serializer.h"
#include "src/heo/include/optimization_direction.h"
#include "src/heo/include/solution.h"

//__________________________________________________________________________________
struct OneMax_Config
{
    double initial_probability_;
    int dimension_;

    explicit OneMax_Config():
        initial_probability_(),
        dimension_()
    {}

    static char const* get_Name() { return "onemax"; }
    static char const* get_Section() { return get_Name(); }

    template<class V>
    void accept(V& v)
    {
        v(VAR_(initial_probability),        0.5);
        v(VAR_(dimension),                  100);
    }
};
//__________________________________________________________________________________
struct OneMax_Problem
{
    static char const* get_Default_File_Name() { return ""; }

    template<class I, class E>
    bool read(I& in, E* env, random_generator& rnd)
    {
        return true;
    }
};
//__________________________________________________________________________________
class OneMax_Solution
{
    //DISALLOW_COPY_AND_ASSIGN(OneMax_Solution);
public:
    typedef maximization<> TDirection;
    typedef int            TCostType;
protected:
    Array<bool> data_;
    TCostType   cost_;
    bool        changed_;

    unsigned    hash_;
    bool        hash_changed_;
public:
    explicit OneMax_Solution():
        cost_(),
        changed_(),
        hash_(),
        hash_changed_()
    {}

    template<class E>
    void init(E* env, random_generator& rnd)
    {
        size_t dim = env->cfg_.dimension_;
        data_.set_Size(dim);
    }

    template<class E>
    void generate(E* env, random_generator& rnd)
    {
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            bool f = rnd.next_Double() < env->cfg_.initial_probability_;
            data_[i] = f;
        }
        changed_ = true;
    }

    TCostType get_Cost() const
    {
        ASSERT_(!changed_);
        return cost_;
    }

    unsigned get_Hash()
    {
        ASSERT_(!changed_);
        if (hash_changed_)
        {
            hash_changed_ = false;
            hash_ = Calc_Hash(&data_[0], data_.size() * sizeof(data_[0]));
        }
        return hash_;
    }

    template<class E>
    void evaluate(E* env, random_generator& rnd)
    {
        if (!changed_)
            return;
        changed_ = false;
        hash_changed_ = true;
        int f = 0;
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            if (data_[i])
                ++f;
        }
        cost_ = f;
    }


    template<class V>
    void accept(V& v)
    {
        v(cost_);
        v(changed_);
        v(data_);
    }

    void assign(OneMax_Solution const& sol)
    {
        ASSERT_(data_.size());
        ASSERT_(data_.size() == sol.data_.size());
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            data_[i] = sol.data_[i];
        }
        changed_        = sol.changed_;
        cost_           = sol.cost_;

        hash_           = sol.hash_;
        hash_changed_   = sol.hash_changed_;
    }

    template<class X>
    void copy_To(X& x) const
    {
        x.assign(*this);
    }

    template<class E>
    void print(std::ostream& os, E* env)
    {
        os << get_Cost() << lf;
    }

    void dump(std::ostream& out)
    {
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            out << data_[i];
        }
    }

    template<class E>
    bool is_Solved(E* env) const
    {
        return cost_ == env->problem_.dimension_;
    }

    template<class E>
    static bool read(E* env, std::string const& file_name, Solution_Pool<OneMax_Solution>& pool)
    {
        std::ifstream in(file_name.c_str(), std::ios::binary);
        if (!in)
        {
            std::cout << "ERROR: can not open '" << file_name << "'" << std::endl;
            return false;
        }
        meta::ifstream_visitor bin_in(in);

        //:read count of solutions
        size_t count;
        bin_in >> count;

        pool.resize(count);
        random_generator rnd(0);
        for (Solution_Pool<OneMax_Solution>::iterator it = pool.sol_list_.begin();
            it != pool.sol_list_.end(); ++it)
        {
            it->init(env, rnd);
            bin_in >> it->data_;
            it->changed_ = true;
            char magic;
            in.read(&magic, 1);
            if ('*' != magic)
                return false;
        }
        return true;
    }

    template<class E>
    void write(E* env, std::string const& file_name, size_t id, size_t count)
    {
        std::ofstream out(file_name.c_str(), (id ? std::ios::binary | std::ios::app : std::ios::binary));
        meta::ofstream_visitor bin_out(out);
        if (!id)
        {//:write count of solutions
            bin_out << count;
        }
        bin_out << data_;
        out.write("*", 1);
    }
};
//__________________________________________________________________________________

class OneMax_Thread_History
{
public:
    std::vector< std::vector<OneMax_Solution> > sol_;
};

//__________________________________________________________________________________
template <class X> class GA;
template <class X> class SHC;

class OneMax_History
{
    std::vector<OneMax_Thread_History> hist_;
public:
    template<class E>
    bool init(E* env, int np)
    {
        hist_.resize(np);
        return true;
    }

    //template<class E, class X>
    //void update(int step, int rank, SA<X>& s, E* env)
    //{
    //}

    template<class E, class X>
    void update(int step, int rank, GA<X>& solver, E* env);

    template<class E, class X>
    void update(int step, int rank, SHC<X>& solver, E* env);

    template<class E, class TSolver>
    void output(E* env, TSolver& solver, char const* tech, int np, int mpi_rank = -1)
    {
        if (!env->output_dir_.length())
            return;
        std::string dir = env->output_dir_;
        std::string tmp;
        meta::Value2String(env->rand_seed_, tmp);
        sys_api::Path_Append(dir, tmp.c_str());
        sys_api::Path_Append(dir, "history");
        sys_api::Create_Dir_Recursive(dir.c_str());
        for (size_t i = 0; i < hist_.size(); ++i)
        {
            std::string path = dir;
            std::string name;
            name = solver.get_Name();
            name += ".";
            name += tech;
            name += ".";
            meta::Value2String(np, tmp);
            name += tmp.c_str();
            name += ".";
            if (mpi_rank < 0)
            {
                meta::Value2String(i, tmp);
            }
            else
            {
                ASSERT_(1 == hist_.size());
                meta::Value2String(mpi_rank, tmp);
            }
            name += tmp.c_str();
            name += ".txt";

            sys_api::Path_Append(path, name.c_str());
            output_Ex(path, hist_[i]);
        }
    }

    void output_Ex(std::string const& path, OneMax_Thread_History& hist)
    {
        std::ofstream out(path.c_str(), std::ios::out);
        for (size_t i = 0; i < hist.sol_.size(); ++i)
        {
            out << i + 1 << std::endl;
            std::vector<OneMax_Solution>& arr = hist.sol_[i];
            for (size_t j = 0; j < arr.size(); ++j)
            {
                out << std::setw(4) << arr[j].get_Cost() << ": ";
                arr[j].dump(out);
                out << lf;
            }
            out << lf;
        }
    }

    void enter_Thread(int rank)
    {
    }

    void leave_Thread(int rank)
    {
    }
};

//EOF!
